#pragma once

#include "exports.h"
#include "Mesh.h"
#include "Texture.h"
#include "Shader.h"
#include "FramebufferObject.h"
#include "RenderbufferObject.h"
#include "PixelbufferObject.h"
#include <array>

class FramebufferObject;
class Parameters;
class RenderbufferObject;
class Camera;

enum ShootParameterData
{
	Positions,
	TexCoords
};

class RADIOSITY_API Radiosity
{
public:
	Radiosity(const Parameters* const parameters);
	Radiosity(const Parameters* const parameters, const Camera* const camera);
	virtual ~Radiosity(void) {}

	bool Iterate(void);
	void Display(void);
	void ChangeView(void);

	float GetCurrentFov(void);

	static void OnFrameDrawTimer(void);
	static void OnFovDecayTimer(void);
private:
	Extensions _extensions;

	Radiosity(const Radiosity&);
	const Radiosity& operator=(const Radiosity&) { return *this; }
	void Create();

	void InitializeOpenGL(void);
	void InitializeBuffers(void);
	void InitializeTextures(void);
	void InitializeShaders(void);
	void InitializeMatrixes(void);
	void InitializeAmbientParameters(void);
	void InitializeFaces(void);
	void InitializeEmissivity(void);
	void InitializeImportance(void);
	void InitializeShootParameters(void);

	void DrawAmbientValue(void);
	const RadiosityMesh::FaceIter GetNextShooter(void);
	void LargestEnergyTest(const Texture2D& nextShooterId);
	GLint ReadShooterId(const Texture2D& nextShooterId);
	void DrawHemisphereItemBuffer(const RadiosityMesh::FaceIter& shooter, const Vec3f& shootPosition);
	void HemisphereShadeFromTexture(const Vec3f& shootPosition, const Vec2f& shootTexCoords);
	void HemisphereShadeFromPoint(const RadiosityMesh::FaceIter& shooter, const Vec3f& shootPosition, const float shooterOffset);
	void HemisphereShade(const Shader& hemisphereShader, const RadiosityAlgorithm shootMode, const RadiosityMesh::FaceIter& shooter, const Vec3f& shootPosition);
	void DrawTesselatedFace(const RadiosityMesh::FaceIter& faceIter);

	void LoadShootParameters(void);
	void DrawShootParameterData(const ShootParameterData dataType);
	float* const GetShootParameterData(const ShootParameterData dataType, RadiosityMesh::FaceVertexIter faceVertexIter, std::tr1::array<GLshort[2], 4>::const_iterator texCoordsIter);
	void GetShootParameters(const Vec2i& shootMipIndex, Vec3f& shootPosition, Vec2f& shootTexCoord);
	void ClearShootTextures(const RadiosityMesh::FaceIter& shooter);
	bool AdvanceIteration(void);
	void ShootFromElement(void);
	void UpdateShootIndices(void);
	void RegenerateDeltaMips(void);

	void ShootDirectlyReceivedImportance(void);
	void DrawUserViewItemBuffer(void);
	void UserViewShade(void);
	void SelectShootingMode(void);

	void DrawFovItemBuffer(void);
	void StreamVisibilityVector(void);
	void GatherUnshotVectors(const Vec2f& shootTexCoords);
	void DrawGatherUnshotVectors(void);
	void RetrieveUnshotVectors(void);
	void DrawRetrieveUnshotVectors(void);
	void ReadBecomingVisibleVectors(void);
	void ShootUnshotVectors(void);
	void ShootUnshotRadiosityFromPoint(const RadiosityMesh::FaceIter faceIter, const Vec4f unshotShootPosition);
	void PrepareForUnshotShooting(void);

	const Parameters* const _parameters;
	const Camera* const _camera;
	RadiosityMesh _mesh;

	static const std::tr1::array<GLshort[2], 4> _normalizedCoords;

	//Buffer objects
	FramebufferObject _FBO;
	RenderbufferObject _nextShooterRBO;
	RenderbufferObject _hemisphereItemBufferRBO;
	RenderbufferObject _userViewItemBufferRBO;
	PixelbufferObject _fovVisibleVectorPBO;

	//Texture objects
	Texture2D _IjStub;
	Texture2D _hemisphereItemBuffer;
	Texture2D _userViewItemBuffer;
	Texture2DRW _ambient;

	TextureRectRW _unshotVectors;
	TextureRectRW _becomingVisibleVectors;
	TextureRectRW _fovVisibleVector;

	//Shader objects
	Shader _displayShader;
	Shader _nextShooterShader;
	Shader _ambientShader;
	Shader _hemisphereTextureShader;
	Shader _hemispherePointShader;
	Shader _stereographicShader;
	Shader _importanceShader;
	Shader _userViewShader;
	Shader _unshotVectorsGatherShader;
	Shader _unshotVectorsRetrieveShader;

	RadiosityAlgorithm _shootMode;
	float _AjSum;
	Vec3f _R;

	static float _fovDecayAngle;

	//Shooter state preservation for forced FPS callback
	Vec2i _shootIndex;
	Vec2i _shootSize;
	RadiosityMesh::FaceIter _shooter;
	vector<GLfloat> _shootPositions;
	vector<GLfloat> _shootTexCoords;

	bool _importanceShootFlag;
	bool _shootingUnshotRadiosity;
	RadiosityMesh::FaceIter _currentUnshotReceiver;
	static bool _fovChangedFlag;
	static bool _frameDrawFlag;
};
